package dom.xpath.query.xpath.internal;

import static dom.xpath.query.xpath.XPText.*;
import dom.xpath.index.Index;
import dom.xpath.query.EvalException;
import dom.xpath.query.xpath.XPContext;
import dom.xpath.query.xpath.expr.Expression;
import dom.xpath.query.xpath.expr.Path;
import dom.xpath.query.xpath.expr.Union;
import dom.xpath.query.xpath.locpath.LocationPath;
import dom.xpath.query.xpath.locpath.Step;
import dom.xpath.query.xpath.values.Bool;
import dom.xpath.query.xpath.values.Comp;
import dom.xpath.query.xpath.values.Literal;
import dom.xpath.query.xpath.values.Item;

/**
 * This Expression assembles a number of similar comparisons of which one
 * has to evluate true.
 *
 * @author Workgroup DBIS, University of Konstanz 2005-07, ISC License
 * @author Christian Gruen
 */
public final class OneOf extends InternalExpr {
  /** First Expression. */
  final LocationPath path;
  /** Second Expression. */
  final Item[] vals;
  /** Comparator. */
  final Comp cmp;

  /**
   * Constructor.
   * @param p location path
   * @param v second expression to compare with first
   * @param c comparator
   */
  public OneOf(final LocationPath p, final Item[] v, final Comp c) {
    path = p;
    vals = v;
    cmp = c;
  }

  @Override
  public Bool eval(final XPContext ctx)
      throws EvalException {

    final Item v = ctx.eval(path);
    if(v.size() == 0) return Bool.FALSE;

    for(final Item val : vals) {
      if(Comp.EQ.eval(v, val)) return Bool.TRUE;
    }
    return Bool.FALSE;
  }

  @Override
  public Expression indexEquivalent(final XPContext ctx, final Step curr) {
    final int vl = vals.length;
    final Expression[] indexExprs = new Expression[vl];

    // find index equivalents
    for(int v = 0; v != vl; v++) {
      final byte[] lit = ((Literal) vals[v]).str();
      indexExprs[v] = new IndexAccess(indexType, lit);
    }

    ctx.compInfo(OPTONEOF);
    return new Path(new Union(indexExprs), path.invertPath(curr));
  }

  @Override
  public int indexSizes(final XPContext ctx, final Step curr,
      final int min) {

    indexType = path.indexable(ctx, vals[0], cmp);
    if(indexType == null) return Integer.MAX_VALUE;

    int sum = 0;
    for(final Item val : vals) {
      final int nrIDs = ctx.local.data.nrIDs(indexType, val.str());
      sum += nrIDs;
      if(sum > min) return min;
    }
    return sum > min ? min : sum;
  }

  /** Index type (0 = none, 1 = text, 2 = word, 3 = attr). */
  private Index.TYPE indexType;

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder(
        getClass().getSimpleName() + "(" + path);
    for(int v = 0; v < vals.length; v++) {
      sb.append((v != 0 ? ", " : " " + cmp + " ") + vals[v]);
    }
    return sb.append(")").toString();
  }
}
